package com.game.core.net.handler;

import java.net.SocketAddress;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//服务器可能处于一直发送消息，但是不需要客户端回复的状态，比如加载战斗
//所以单纯的依靠write_idle已经不可靠了，在read_idle时，不能直接断开，需要有缓冲
public class HeartBeatHandler extends ChannelDuplexHandler 
{
	private static final Logger logger = LoggerFactory.getLogger(HeartBeatHandler.class);
	
	private ConcurrentHashMap<SocketAddress, Integer> clientMaps = new ConcurrentHashMap<SocketAddress, Integer>();

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception
	{
		if (evt instanceof IdleStateEvent)
		{
			IdleStateEvent event = (IdleStateEvent) evt;
			if (event.state().equals(IdleState.READER_IDLE))
			{
				SocketAddress client = ctx.channel().remoteAddress();
				Integer count = clientMaps.get(client);
				if (count == null)
				{
					//第一次idle不断开
					count = 1;
					clientMaps.put(client, count);
//					logger.debug("recv {} reader_idle then send ping cmd", ctx.channel().remoteAddress());
					ByteBuf outBuf = ctx.alloc().buffer(1);
	    			outBuf.writeByte(1);
	    			ctx.writeAndFlush(outBuf);
				}
				else
				{
					//第二次reader_idle断开连接
//					logger.warn("recv reader_idle {} then close channel", ctx.channel().remoteAddress());
					// do nothing 等待逻辑超时
					ctx.channel().close();
					clientMaps.remove(client);
				}
			}
			else if (event.state().equals(IdleState.WRITER_IDLE))
			{
				//do nothing,因为战斗加载时，服务器推送给客户端消息，客户端不需要应答
//				logger.debug("recv writer_idle {} then send ping cmd", ctx.channel().remoteAddress());
//				ByteBuf outBuf = ctx.alloc().buffer(1);
//    			outBuf.writeByte(1);
//    			ctx.writeAndFlush(outBuf);
			}
			else if (event.state().equals(IdleState.ALL_IDLE))
			{
				logger.warn("HeartBeat ALL_IDLE but !!!not set!!! to {} local {}", ctx.channel().remoteAddress(), ctx.channel().localAddress());
			}
		}
		
		ctx.fireUserEventTriggered(evt);
	}

	@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
    {
		//收到对方消息，重置计数
		SocketAddress client = ctx.channel().remoteAddress();
		clientMaps.remove(client);
		
	    if (msg instanceof ByteBuf)
	    {
	    	ByteBuf buf = (ByteBuf)msg;
	    	if (buf.readableBytes() == 1)
	    	{
	    		int cmd = buf.readByte();
	    		if (cmd == 1)
	    		{
	    			ByteBuf outBuf = ctx.alloc().buffer(1);
	    			outBuf.writeByte(2);
	    			ctx.writeAndFlush(outBuf);
//	    			logger.debug("recv remote {} ping then send back", ctx.channel().remoteAddress());
	    		}
	    		else if (cmd == 2)
	    		{
	    			//do nothing;
//	    			logger.debug("recv remote {} ping back", ctx.channel().remoteAddress());
	    		}
	    		
	    		return;
	    	}
	    }
	    
	    try
	    {
	    	ctx.fireChannelRead(msg);
	    }
	    catch (Exception e)
	    {
	    	logger.error("Exception err={}",e.toString());
	    }
	    catch (Throwable a)
	    {
	    	logger.error("Throwable={}",a.toString());
	    }
    }
}
